/*
   Copyright (c) 2023 李伟国
   shiro-learner is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2. 
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
   See the Mulan PSL v2 for more details. 
*/

package cn.learner.config.shiro;

import cn.learner.config.shiro.jwt.JWTToken;
import cn.learner.entity.Permission;
import cn.learner.entity.Role;
import cn.learner.entity.User;
import cn.learner.service.IPermissionService;
import cn.learner.service.IRoleService;
import cn.learner.service.IUserService;
import cn.learner.utils.JWTUtil;
import cn.learner.utils.RedisUtil;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@Component
public class LearnerJWTJdbcRealm extends AuthorizingRealm {
    private static final Logger log = LoggerFactory.getLogger(LearnerJWTJdbcRealm.class);
    @Autowired
    private IUserService userService;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private IPermissionService permissionService;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if(principals == null){
            throw new AuthorizationException("PrincipalCollection方法参数不能为空.");
        }
        String jwt = (String)this.getAvailablePrincipal(principals);
        String username = JWTUtil.getUsername(jwt);
        Set<String> roleNameSet = new LinkedHashSet<>();  // 角色集合
        Set<String> permissionSet = new LinkedHashSet<>();  // 授权集合
        System.out.println(username);
        List<Role> roles = roleService.findByUsername(username);
        for(Role role: roles){
            roleNameSet.add(role.getName());
            List<Permission> permissionList = permissionService.findByRoleId(role.getId());
            for(Permission permission : permissionList){
                permissionSet.add(permission.getAuthorization());
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNameSet);
        info.setStringPermissions(permissionSet);
        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("=== 认证 ===");
        String jwt = (String) token.getCredentials();
        String username = null;
        username = JWTUtil.getUsername(jwt);
        if(username == null){
            throw new AuthenticationException("token中无用户");
        }
        User user = userService.findByUsername(username);
        if(user == null){
            throw new AuthenticationException("用户不存在");
        }
        if(redisUtil.hasKey(username)){
            if(!JWTUtil.verify(jwt)){
                throw new TokenExpiredException("token认证失败，token过期，重新登录",null);
            }else{
                Long current = (Long) redisUtil.get(username);
                if(current.equals(JWTUtil.getExpire(jwt))){
                    redisUtil.expire(username,30*60);
                    return new SimpleAuthenticationInfo(jwt, jwt, "LearnerJWTJdbcRealm");
                }
                else{
                    throw new AuthenticationException("token失效，请重新登录");
                }
            }
        }else{
            throw new AuthenticationException("token过期或者Token错误！");
        }
    }
}
